﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Mjollnir.Live;
using Mjollnir.Live.Controls;

namespace SkyDriveWindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        class ListViewItemSorter : IComparer
        {
            public int Compare(object x, object y)
            {
                var item1 = (SkyDriveItem)((ListViewItem)x).Tag;
                var item2 = (SkyDriveItem)((ListViewItem)y).Tag;

                return (item1.Type == item2.Type)
                    ? string.Compare(item1.Name, item2.Name)
                    : item1.IsFolder ? -1 : 1;
            }
        }

        public Form1()
        {
            InitializeComponent();

            this.itemsListView.ListViewItemSorter = new ListViewItemSorter();

            this.signInLinkLabel.LinkClicked += (s, e) => this.SignIn();
            this.signOutLinkLabel.LinkClicked += (s, e) => this.SignOut();

            this.addFilesLinkLabel.LinkClicked += (s, e) => this.AddFiles();
            this.newFolderLinkLabel.LinkClicked += (s, e) => this.NewFolder();

            this.parentFolderLabel.LinkClicked += (s, e) => this.ParentFolder();

            this.openFolderLinkLabel.LinkClicked += (s, e) => this.OpenItem((SkyDriveItem)this.folderTaskPanel.Tag);
            this.renameFolderLinkLabel.LinkClicked += (s, e) => this.RenameItem((SkyDriveItem)this.folderTaskPanel.Tag);
            this.deleteFolderLinkLabel.LinkClicked += (s, e) => this.DeleteItem((SkyDriveItem)this.folderTaskPanel.Tag);

            this.downloadFileLinkLabel.LinkClicked += (s, e) => this.OpenItem((SkyDriveItem)this.folderTaskPanel.Tag);
            this.renameFileLinkLabel.LinkClicked += (s, e) => this.RenameItem((SkyDriveItem)this.folderTaskPanel.Tag);
            this.deleteFileLinkLabel.LinkClicked += (s, e) => this.DeleteItem((SkyDriveItem)this.folderTaskPanel.Tag);

            this.SignOut();
        }

        LiveConnectSession session;

        List<FolderSpec> folderSpecs = new List<FolderSpec>();

        void SignIn()
        {
            this.SignOut();

            using (var dialog = new SignInDialog())
            {
                // Update the ClientID with your app client Id that you created from https://account.live.com/developers/applications.
                //dialog.ClientId = "%YourAppClientId%";
                dialog.ClientId = File.ReadAllLines(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), @"..\Dropbox\Documents\Keys\PseudoLiveSDK_ClientID.txt")).First().Trim();
                //dialog.Locale = "ja";
                dialog.Scopes = new[] { "wl.signin", "wl.skydrive", "wl.skydrive_update" };

                if (dialog.ShowDialog() != DialogResult.OK)
                {
                    if (dialog.Error == null)
                    {
                        MessageBox.Show("Cancelled.");
                    }
                    else
                    {
                        MessageBox.Show(dialog.Error.Message);
                    }

                    this.SignOut();
                    return;
                }

                this.session = dialog.Session;

                this.commandBarPanel.Enabled = true;
                this.bodyPanel.Enabled = true;

                this.signInLinkLabel.Enabled = false;
                this.signOutLinkLabel.Enabled = true;

                var node = this.navigationBarTreeView.Nodes[0];
                var spec = node.Name.Split(':');

                var id = spec[0];
                var path = spec[1];
                var name = node.Text;

                this.GetItemsAsync(new FolderSpec { Id = id, Path = path, Name = name });
            }
        }

        void SignOut()
        {
            this.session = null;
            this.folderSpecs.Clear();

            this.itemsListView.Items.Clear();

            this.signInLinkLabel.Enabled = true;
            this.signOutLinkLabel.Enabled = false;

            this.currentFolderNameLabel.Text = string.Empty;

            this.progressTimer.Enabled = false;
            this.operationProgressBar.Visible = false;
            this.operationProgressBar.Value = 0;
            this.operationProgressBar.Style = ProgressBarStyle.Marquee;

            this.commandBarPanel.Enabled = false;
            this.bodyPanel.Enabled = false;

            this.taskPanel.Visible = false;
        }

        void ParentFolder()
        {
            if (this.folderSpecs.Count > 1)
            {
                this.folderSpecs.RemoveAt(this.folderSpecs.Count - 1);
            }

            var spec = this.folderSpecs.Last();

            this.GetItemsAsync(spec);
        }

        void GetItemsAsync(FolderSpec spec)
        {
            var client = new LiveConnectClient(this.session);

            client.GetCompleted += this.GetItemsCompleted;
            client.GetAsync(spec.Path, spec);

            this.Busy(true);
        }

        void GetItemsCompleted(object sender, LiveOperationCompltedEventArgs e)
        {
            if (e.Error == null && !e.Cancelled)
            {
                var spec = (FolderSpec)e.UserState;

                if (this.folderSpecs.Count < 1 || this.folderSpecs.Last().Id != spec.Id)
                {
                    this.folderSpecs.Add(spec);
                }

                this.currentFolderNameLabel.Text = spec.Name;

                this.itemsListView.Items.Clear();

                var items = ((object[])e.Result["data"]).Cast<IDictionary<string, object>>();

                foreach (var item in items)
                {
                    var si = new SkyDriveItem(item);

                    var lvi = new ListViewItem(si.Name);

                    lvi.Tag = si;
                    lvi.ImageIndex = (si.IsFolder || si.IsAlbum) ? 1 : 0;

                    this.itemsListView.Items.Add(lvi);

                    lvi.SubItems.Add(si.UpdatedTime.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"));
                    lvi.SubItems.Add(si.SharedWith);
                }

                this.itemsListView.Sort();
            }
            else
            {
                Form1.ShowErrorOrCancel(e);
            }

            this.Ready();
        }

        void OpenItem(SkyDriveItem item)
        {
            if (item.IsFolder || item.IsAlbum)
            {
                var path = item.Id + "/files";

                this.GetItemsAsync(new FolderSpec { Id = item.Id, Path = item.Id + "/files", Name = item.Name });
            }
            else
            {
                this.saveFileDialog1.FileName = item.Name;

                if (this.saveFileDialog1.ShowDialog() != DialogResult.OK) return;

                var path = item.Id + "/content";
                var fileName = this.saveFileDialog1.FileName;

                this.DownloadItemAsync(path, fileName);
            }
        }

        void DownloadItemAsync(string path, string fileName)
        {
            var client = new LiveConnectClient(this.session);

            client.DownloadProgressChanged += this.DownloadItemProgressChanged;
            client.DownloadCompleted += this.DownloadItemCompleted;
            client.DownloadAsync(path, fileName);

            //this.timer1.Enabled = true;
            this.operationProgressBar.Visible = true;
            this.operationProgressBar.Value = 0;
            this.operationProgressBar.Style = ProgressBarStyle.Continuous;

            this.commandBarPanel.Enabled = false;
            this.bodyPanel.Enabled = false;
        }

        void DownloadItemProgressChanged(object sender, LiveDownloadProgressChangedEventArgs e)
        {
            this.operationProgressBar.Value = e.ProgressPercentage;
        }

        void DownloadItemCompleted(object sender, LiveDownloadCompltedEventArgs e)
        {
            if (e.Error == null && !e.Cancelled)
            {
                var fileName = (string)e.UserState;

                try
                {
                    File.WriteAllBytes(fileName, ((MemoryStream)e.Result).ToArray());
                }
                catch (Exception x)
                {
                    MessageBox.Show(x.Message);
                }
            }
            else
            {
                Form1.ShowErrorOrCancel(e);
            }

            this.Ready();
        }

        void AddFiles()
        {
            this.openFileDialog1.FileName = null;

            if (this.openFileDialog1.ShowDialog() != DialogResult.OK) return;

            var path = this.folderSpecs.Last().Id + "/files";
            var fileName = this.openFileDialog1.FileName;
            var stream = default(Stream);

            try
            {
                stream = File.OpenRead(fileName);
            }
            catch (Exception x)
            {
                MessageBox.Show(x.Message);
                return;
            }

            var client = new LiveConnectClient(this.session);

            client.UploadProgressChanged += this.AddFilesProgressChanged;
            client.UploadCompleted += this.AddFilesCompleted;
            client.UploadAsync(path, fileName, stream, stream);

            this.Busy(false);
        }

        void AddFilesProgressChanged(object sender, LiveUploadProgressChangedEventArgs e)
        {
            this.operationProgressBar.Value = e.ProgressPercentage;
        }

        void AddFilesCompleted(object sender, LiveOperationCompltedEventArgs e)
        {
            var stream = (Stream)e.UserState;

            stream.Close();

            if (e.Error == null && !e.Cancelled)
            {
                this.GetItemsAsync(this.folderSpecs.Last());
            }
            else
            {
                Form1.ShowErrorOrCancel(e);
                this.Ready();
            }
        }

        void NewFolder()
        {
            using (var dialog = new Form2 { Title = "New folder", Prompt = "Folder name:", UserInput = "New Folder" })
            {
                if (dialog.ShowDialog() != DialogResult.OK) return;

                var path = this.folderSpecs.Last().Id;
                var body = new Dictionary<string, object> { { "name", dialog.UserInput } };

                var client = new LiveConnectClient(this.session);

                client.PostCompleted += this.NewFolderCompleted;
                client.PostAsync(path, body);

                this.Busy(true);
            }
        }

        void NewFolderCompleted(object sender, LiveOperationCompltedEventArgs e)
        {
            if (e.Error == null && !e.Cancelled)
            {
                this.GetItemsAsync(this.folderSpecs.Last());
            }
            else
            {
                Form1.ShowErrorOrCancel(e);
                this.Ready();
            }
        }

        void RenameItem(SkyDriveItem item)
        {
            using (var dialog = new Form2 { Title = "Rename", Prompt = "New name:", UserInput = item.Name })
            {
                if (dialog.ShowDialog() != DialogResult.OK) return;

                var path = item.Id;
                var body = new Dictionary<string, object> { { "name", dialog.UserInput } };

                var client = new LiveConnectClient(this.session);

                client.PutCompleted += this.RenameItemCompleted;
                client.PutAsync(path, body);

                this.Busy(true);
            }
        }

        void RenameItemCompleted(object sender, LiveOperationCompltedEventArgs e)
        {
            if (e.Error == null && !e.Cancelled)
            {
                this.GetItemsAsync(this.folderSpecs.Last());
            }
            else
            {
                Form1.ShowErrorOrCancel(e);
                this.Ready();
            }
        }

        void DeleteItem(SkyDriveItem item)
        {
            var message = string.Format(@"Are you sure you want to delete ""{0}"" ?", item.Name);
            var caption = "Delete";

            if (MessageBox.Show(message, caption, MessageBoxButtons.YesNo) != DialogResult.Yes) return;

            var path = item.Id;

            var client = new LiveConnectClient(this.session);

            client.DeleteCompleted += this.DeleteItemCompleted;
            client.DeleteAsync(path);

            this.Busy(true);
        }

        void DeleteItemCompleted(object sender, LiveOperationCompltedEventArgs e)
        {
            if (e.Error == null && !e.Cancelled)
            {
                this.GetItemsAsync(this.folderSpecs.Last());
            }
            else
            {
                Form1.ShowErrorOrCancel(e);
                this.Ready();
            }
        }

        void Ready()
        {
            this.taskPanel.Visible = false;

            this.progressTimer.Enabled = false;
            this.operationProgressBar.Visible = false;
            this.operationProgressBar.Value = 0;
            this.operationProgressBar.Style = ProgressBarStyle.Marquee;

            this.commandBarPanel.Enabled = true;
            this.bodyPanel.Enabled = true;
        }

        void Busy(bool useProgressTimer)
        {
            this.operationProgressBar.Visible = true;
            this.operationProgressBar.Value = 0;
            this.operationProgressBar.Style = useProgressTimer ? ProgressBarStyle.Marquee : ProgressBarStyle.Continuous;
            this.progressTimer.Enabled = useProgressTimer;

            this.commandBarPanel.Enabled = false;
            this.bodyPanel.Enabled = false;
        }

        static void ShowErrorOrCancel(LiveOperationCompltedEventArgs e)
        {
            if (e.Cancelled)
            {
                MessageBox.Show("Cancelled");
            }
            else
            {
                MessageBox.Show(e.Error.Message);
            }
        }

        static void ShowErrorOrCancel(LiveDownloadCompltedEventArgs e)
        {
            if (e.Cancelled)
            {
                MessageBox.Show("Cancelled");
            }
            else
            {
                MessageBox.Show(e.Error.Message);
            }
        }

        void ParepareTaskPanel()
        {
            var item = (this.itemsListView.SelectedItems.Count > 0) ? (SkyDriveItem)(this.itemsListView.SelectedItems[0]).Tag : null;

            this.taskPanel.Visible = item != null;

            if (item != null)
            {
                var isFolder = item.IsFolder || item.IsAlbum;

                this.folderTaskPanel.Visible = isFolder;
                this.fileTaskPanel.Visible = !isFolder;

                this.folderNameLabel.Text = item.Name;
                this.fileNameLabel.Text = item.Name;
            }

            this.folderTaskPanel.Tag = item;
            this.fileTaskPanel.Tag = item;
        }

        private void ProgressTimer_Tick(object sender, EventArgs e)
        {
            var value = this.operationProgressBar.Value;

            if (value == 100)
            {
                this.operationProgressBar.Value = 0;
            }
            else
            {
                this.operationProgressBar.Value = value + 1;
            }

        }

        private void HeaderPanel_Paint(object sender, PaintEventArgs e)
        {
            var rect1 = e.ClipRectangle;

            rect1.Height = 20;

            e.Graphics.FillRectangle(Brushes.LightBlue, rect1);

            var rect2 = e.ClipRectangle;

            rect2.Height = 16;
            rect2.Offset(0, 20);

            using (var brush = new LinearGradientBrush(rect2, Color.LightBlue, this.headerPanel.BackColor, LinearGradientMode.Vertical))
            {
                e.Graphics.FillRectangle(brush, rect2);
            }
        }

        private void NavigationBarTreeView_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            var node = e.Node;
            if (node == null) return;

            var spec = node.Name.Split(':');

            var id = spec[0];
            var path = spec[1];
            var name = node.Text;

            this.GetItemsAsync(new FolderSpec { Id = id, Path = path, Name = name });
        }

        private void ItemsListView_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            this.ParepareTaskPanel();
        }

        private void ItemsListView_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left) return;

            var info = this.itemsListView.HitTest(e.Location);
            if (info.Item == null) return;

            this.OpenItem((SkyDriveItem)info.Item.Tag);
        }
    }
}
